﻿// 酷狗音乐.cpp : 定义应用程序的入口点。
//
#pragma execution_character_set("utf-8")  


#include "framework.h"
#include "酷狗音乐.h"


// 数据
// 图形设备，管理GPU资源
static ID3D11Device* g_pd3dDevice = nullptr;
// 设备上下文，执行渲染命令
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
// 交换链，管理前后缓冲区的切换
static IDXGISwapChain* g_pSwapChain = nullptr;
// 标记交换链是否被遮挡 当窗口被其他窗口遮挡或最小化时，设置为true 防止在窗口不可见时继续渲染，节省GPU资源
static bool                     g_SwapChainOccluded = false;
// 存储窗口调整大小后的新尺寸 当用户拖拽窗口边缘改变大小时，记录新的宽度和高度 确保界面能够正确适应新的窗口尺寸
static UINT                     g_ResizeWidth = 0, g_ResizeHeight = 0;
// 主渲染目标视图 定义GPU渲染的目标位置（通常是屏幕） 所有ImGui界面元素都渲染到这个目标上
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;

// 辅助函数的前向声明

/**
 * 创建DirectX 11设备和交换链
 * 
 * 功能：
 * - 初始化DirectX 11图形设备
 * - 创建交换链管理前后缓冲区
 * - 设置渲染目标格式和刷新率
 * - 如果硬件不支持，自动回退到WARP软件渲染器
 * 
 * 参数：
 * - hWnd: 窗口句柄，用于关联交换链和窗口
 * 
 * 返回值：
 * - true: 设备创建成功
 * - false: 设备创建失败
 */
bool CreateDeviceD3D(HWND hWnd);

/**
 * 清理DirectX 11设备资源
 * 
 * 功能：
 * - 释放交换链资源
 * - 释放设备上下文
 * - 释放图形设备
 * - 防止内存泄漏
 * 
 * 参数：
 * - 无
 * 
 * 返回值：
 * - 无
 */
void CleanupDeviceD3D();

/**
 * 创建渲染目标视图
 * 
 * 功能：
 * - 从交换链获取后缓冲区
 * - 创建渲染目标视图
 * - 设置GPU渲染的目标位置
 * - 为ImGui界面提供渲染表面
 * 
 * 参数：
 * - 无
 * 
 * 返回值：
 * - 无
 */
void CreateRenderTarget();

/**
 * 清理渲染目标资源
 * 
 * 功能：
 * - 释放渲染目标视图
 * - 在窗口大小改变时调用
 * - 为新尺寸的渲染目标做准备
 * 
 * 参数：
 * - 无
 * 
 * 返回值：
 * - 无
 */
void CleanupRenderTarget();

/**
 * Windows消息处理函数
 * 
 * 功能：
 * - 处理窗口消息（鼠标、键盘、窗口事件）
 * - 将消息转发给ImGui处理
 * - 处理窗口大小改变事件
 * - 处理窗口关闭事件
 * 
 * 参数：
 * - hWnd: 窗口句柄
 * - msg: 消息类型
 * - wParam: 消息参数1
 * - lParam: 消息参数2
 * 
 * 返回值：
 * - LRESULT: 消息处理结果
 */
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);



int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    
    // 消除编译器警告：标记未使用的参数
    // hPrevInstance: 前一个实例句柄，在现代Windows中总是NULL，因为每个进程都是独立的
    // lpCmdLine: 命令行参数，本程序不需要处理命令行输入
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);


    // 使进程支持DPI感知并获取主显示器缩放比例
    ImGui_ImplWin32_EnableDpiAwareness();
    float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY));

    // 创建应用程序窗口
    WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };
    ::RegisterClassExW(&wc);
    HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr);

    // 初始化Direct3D
    if (!CreateDeviceD3D(hwnd))
    {
        CleanupDeviceD3D();
        ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
        return 1;
    }

    // 显示窗口
    ::ShowWindow(hwnd, SW_SHOWDEFAULT);
    ::UpdateWindow(hwnd);

    // 设置Dear ImGui上下文
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // 启用键盘控制
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // 启用手柄控制
    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // 启用停靠
    io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // 启用多视口/平台窗口

    // 启用多视口模式：每个ImGui窗口都可以成为独立的系统窗口
    // 当设置为true时，ImGui窗口可以拖出主窗口，成为独立的系统窗口
    //io.ConfigViewportsNoAutoMerge = true;
    // 禁用任务栏图标：不在任务栏为每个视口显示独立图标
    // 当设置为true时，只有主窗口会在任务栏显示图标
    //io.ConfigViewportsNoTaskBarIcon = true;
    // 禁用默认父窗口：视口不关联到主窗口
    // 当设置为true时，视口可以完全独立，不受主窗口影响
    //io.ConfigViewportsNoDefaultParent = true;
    // 强制显示标签栏：停靠窗口总是显示标签栏
    // 当设置为true时，所有停靠窗口都会显示标签栏，即使只有一个窗口
    //io.ConfigDockingAlwaysTabBar = true;
    // 启用透明拖拽：拖拽窗口时显示透明效果
    // 当设置为true时，拖拽窗口时会显示半透明的预览效果
    //io.ConfigDockingTransparentPayload = true;

    // 设置Dear ImGui样式
    //ImGui::StyleColorsDark();
    // 使用经典样式
	ImGui::StyleColorsClassic();
    //ImGui::StyleColorsLight();

    // 设置缩放
    ImGuiStyle& style = ImGui::GetStyle();
    style.ScaleAllSizes(main_scale);        // 烘焙固定的样式缩放。（在我们有动态样式缩放解决方案之前，更改此设置需要重置样式并再次调用此函数）
    style.FontScaleDpi = main_scale;        // 设置初始字体缩放。（使用io.ConfigDpiScaleFonts=true使此设置不必要。我们在这里保留两者用于文档目的）
    io.ConfigDpiScaleFonts = true;          // [实验性] 当显示器DPI更改时，在Begin()中自动覆盖style.FontScaleDpi。这将缩放字体但暂时_不_缩放大小/填充。
    io.ConfigDpiScaleViewports = true;      // [实验性] 当显示器DPI更改时缩放Dear ImGui和平台窗口。

    // 当启用视口时，我们调整WindowRounding/WindowBg，使平台窗口看起来与常规窗口相同。
    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
    {
        style.WindowRounding = 0.0f;
        style.Colors[ImGuiCol_WindowBg].w = 1.0f;
    }

    // 设置平台/渲染器后端
    ImGui_ImplWin32_Init(hwnd);
    ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);

    
	ImFontConfig fontCfg; // 创建字体配置对象
    fontCfg.FontDataOwnedByAtlas = false;
	ImFont* consolaFont = io.Fonts->AddFontFromMemoryTTF((void*)msyh_data, msyh_size, 16.0f, &fontCfg, io.Fonts->GetGlyphRangesChineseFull()); // 添加Consolas字体，使用压缩数据和大小
    //判断一下字体有没有加载成功
    IM_ASSERT(consolaFont != nullptr);


    // 我们的状态
    bool show_demo_window = true;
    bool show_another_window = true;
    bool 主窗口是否打开 = true;
    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

    // 主循环
    bool done = false;
    while (!done)
    {
        // 轮询和处理消息（输入、窗口调整大小等）
        // 请参阅下面的WndProc()函数，了解如何将事件分派到Win32后端。
        MSG msg;
        while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
        {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
            if (msg.message == WM_QUIT)
                done = true;
        }
        if (done)
            break;

        // 处理窗口最小化或屏幕锁定
        if (g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
        {
            ::Sleep(10);
            continue;
        }
        g_SwapChainOccluded = false;

        // 处理窗口调整大小（我们不直接在WM_SIZE处理程序中调整大小）
        if (g_ResizeWidth != 0 && g_ResizeHeight != 0)
        {
            CleanupRenderTarget();
            g_pSwapChain->ResizeBuffers(0, g_ResizeWidth, g_ResizeHeight, DXGI_FORMAT_UNKNOWN, 0);
            g_ResizeWidth = g_ResizeHeight = 0;
            CreateRenderTarget();
        }

        // 开始Dear ImGui帧
        ImGui_ImplDX11_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();

        主窗口::渲染主窗口UI();

        // 渲染
        // 1. 准备ImGui数据
        ImGui::Render();
        // 2. 计算背景颜色
        const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
        // 3. 设置渲染目标（屏幕）
        g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
        // 4. 清除屏幕，用背景色填充
        g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);
        // 5. 渲染ImGui界面到屏幕
        ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

        // 更新和渲染其他平台窗口
        if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
        {
            ImGui::UpdatePlatformWindows();
            ImGui::RenderPlatformWindowsDefault();
        }

        // 呈现
        HRESULT hr = g_pSwapChain->Present(1, 0);   // 带垂直同步的呈现
        //HRESULT hr = g_pSwapChain->Present(0, 0); // 不带垂直同步的呈现
        g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
    }

    // 清理
    ImGui_ImplDX11_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();

    CleanupDeviceD3D();
    ::DestroyWindow(hwnd);
    ::UnregisterClassW(wc.lpszClassName, wc.hInstance);

    return 0;

}

// 辅助函数

/**
 * 创建DirectX 11设备和交换链
 * 
 * 功能：
 * - 初始化DirectX 11图形设备
 * - 创建交换链管理前后缓冲区
 * - 设置渲染目标格式和刷新率
 * - 如果硬件不支持，自动回退到WARP软件渲染器
 * 
 * 参数：
 * - hWnd: 窗口句柄，用于关联交换链和窗口
 * 
 * 返回值：
 * - true: 设备创建成功
 * - false: 设备创建失败
 */
bool CreateDeviceD3D(HWND hWnd)
{
    // 设置交换链
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 2;
    sd.BufferDesc.Width = 0;
    sd.BufferDesc.Height = 0;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = hWnd;
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    UINT createDeviceFlags = 0;
    //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
    D3D_FEATURE_LEVEL featureLevel;
    const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
    HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
    if (res == DXGI_ERROR_UNSUPPORTED) // 如果硬件不可用，尝试高性能WARP软件驱动程序。
        res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
    if (res != S_OK)
        return false;

    CreateRenderTarget();
    return true;
}

/**
 * 清理DirectX 11设备资源
 * 
 * 功能：
 * - 释放交换链资源
 * - 释放设备上下文
 * - 释放图形设备
 * - 防止内存泄漏
 * 
 * 参数：
 * - 无
 * 
 * 返回值：
 * - 无
 */
void CleanupDeviceD3D()
{
    CleanupRenderTarget();
    if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; }
    if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; }
    if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
}

/**
 * 创建渲染目标视图
 * 
 * 功能：
 * - 从交换链获取后缓冲区
 * - 创建渲染目标视图
 * - 设置GPU渲染的目标位置
 * - 为ImGui界面提供渲染表面
 * 
 * 参数：
 * - 无
 * 
 * 返回值：
 * - 无
 */
void CreateRenderTarget()
{
    ID3D11Texture2D* pBackBuffer;
    g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
    g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
    pBackBuffer->Release();
}

/**
 * 清理渲染目标资源
 * 
 * 功能：
 * - 释放渲染目标视图
 * - 在窗口大小改变时调用
 * - 为新尺寸的渲染目标做准备
 * 
 * 参数：
 * - 无
 * 
 * 返回值：
 * - 无
 */
void CleanupRenderTarget()
{
    if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; }
}

// 从imgui_impl_win32.cpp前向声明消息处理程序
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Win32消息处理程序
// 您可以读取io.WantCaptureMouse、io.WantCaptureKeyboard标志来判断dear imgui是否想要使用您的输入。
// - 当io.WantCaptureMouse为true时，不要将鼠标输入数据分派到您的主应用程序，或清除/覆盖您的鼠标数据副本。
// - 当io.WantCaptureKeyboard为true时，不要将键盘输入数据分派到您的主应用程序，或清除/覆盖您的键盘数据副本。
// 通常，您可能总是将所有输入传递给dear imgui，并根据这两个标志从您的应用程序中隐藏它们。

/**
 * Windows消息处理函数
 * 
 * 功能：
 * - 处理窗口消息（鼠标、键盘、窗口事件）
 * - 将消息转发给ImGui处理
 * - 处理窗口大小改变事件
 * - 处理窗口关闭事件
 * 
 * 参数：
 * - hWnd: 窗口句柄
 * - msg: 消息类型
 * - wParam: 消息参数1
 * - lParam: 消息参数2
 * 
 * 返回值：
 * - LRESULT: 消息处理结果
 */
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
        return true;

    switch (msg)
    {
    case WM_SIZE:
        if (wParam == SIZE_MINIMIZED)
            return 0;
        g_ResizeWidth = (UINT)LOWORD(lParam); // 排队调整大小
        g_ResizeHeight = (UINT)HIWORD(lParam);
        return 0;
    case WM_SYSCOMMAND:
        if ((wParam & 0xfff0) == SC_KEYMENU) // 禁用ALT应用程序菜单
            return 0;
        break;
    case WM_DESTROY:
        ::PostQuitMessage(0);
        return 0;
    }
    return ::DefWindowProcW(hWnd, msg, wParam, lParam);
}
